using System;
using System.Collections;
using System.Diagnostics;

namespace Qiang.Topology.Geometries
{
    /// <summary>
    ///	Useful utility functions for handling Coordinate arrays
    /// </summary>
    public class CoordinateArrays
    {
        /// <summary>
        /// Only static methods!
        /// </summary>
        private CoordinateArrays() { }

        /// <summary>
    	/// Creates a deep copy of the argument <c>Coordinate</c> array.
        /// </summary>
        /// <param name="coordinates">Array of Coordinates.</param>
        /// <returns>Deep copy of the input.</returns>
        public static Coordinate[] CopyDeep(Coordinate[] coordinates)
        {
	        Coordinate[] copy = new Coordinate[coordinates.Length];            
	        for(int i = 0; i < coordinates.Length; i++)            
            	copy[i] = new Coordinate(coordinates[i]);            
            return copy;
        }

        /// <summary>
        /// Converts the given IList of Coordinates into a Coordinate array.
        /// </summary>
        /// <param name="coordList">IList of coordinates</param>
        public static Coordinate[] ToCoordinateArray(IList coordList)
        {
            ArrayList tempList = new ArrayList(coordList);
       	    return (Coordinate[])tempList.ToArray(typeof(Coordinate));
        }

        /// <summary>
        /// Returns whether Equals returns true for any two consecutive
        /// coordinates in the given array.
        /// </summary>
        /// <param name="coord">Array of Coordinates.</param>
        /// <returns>true if coord has repeated points; false otherwise.</returns>
      	public static bool HasRepeatedPoints(Coordinate[] coord)
      	{
            for(int i = 1; i < coord.Length; i++)            
            	if(coord[i - 1].Equals(coord[i]))
                    return true;            	
	        return false;
      	}

        /// <summary>
        /// Returns either the given coordinate array if its length is greater than
        /// the given amount, or an empty coordinate array.
        /// </summary>
        /// <param name="n">Length amount.</param>
        /// <param name="c">Array of Coordinates.</param>
        /// <returns>New Coordinate array.</returns>
      	public static Coordinate[] AtLeastNCoordinatesOrNothing(int n, Coordinate[] c)
      	{
            return (c.Length >= n) ? (c) : (new Coordinate[] { });
        }

        /// <summary>
        /// If the coordinate array argument has repeated points,
        /// constructs a new array containing no repeated points.
        /// Otherwise, returns the argument.
        /// </summary>
        /// <param name="coord"></param>
        /// <returns></returns>
        /// <seealso cref="CoordinateArray.HasRepeatedPoints"/>
      	public static Coordinate[] RemoveRepeatedPoints(Coordinate[] coord)
      	{
            if (!HasRepeatedPoints(coord))
                return coord;
            CoordinateList coordList = new CoordinateList(coord, false);
            return coordList.ToCoordinateArray();
      	}

        /// <summary>
        /// Reverses the coordinates in an array in-place.
        /// </summary>
        /// <param name="coord">Array of Coordinates.</param>
      	public static void Reverse(Coordinate[] coord)
      	{
            int last = coord.Length - 1;
            int mid = last / 2;
            for (int i = 0; i <= mid; i++)
            {
                Coordinate tmp = coord[i];
                coord[i] = coord[last - i];
                coord[last - i] = tmp;
            }
      	}

        /// <summary>
        /// Returns true if the two arrays are identical, both null, or pointwise
        /// equal (as compared using Coordinate.Equals).
        /// </summary>
        /// <param name="coord1">First array of Coordinates.</param>
        /// <param name="coord2">Second array of Coordinates.</param>
        /// <returns><c>true</c> if two Coordinates array are equals; false otherwise</returns>
        /// <seealso cref="Coordinate.Equals"/>
      	public static bool Equals(Coordinate[] coord1, Coordinate[] coord2)
      	{
            if(coord1 == coord2) 
                return true;
            if(coord1 == null || coord2 == null) 
                return false;
            if(coord1.Length != coord2.Length) 
                return false;
            for (int i = 0; i < coord1.Length; i++)
            	if (!coord1[i].Equals(coord2[i])) 
                    return false;            
            return true;
        }

        /// <summary>
        /// Returns the minimum coordinate, using the usual lexicographic comparison.
        /// </summary>
        /// <param name="coordinates">Array to search.</param>
        /// <returns>The minimum coordinate in the array, found using <c>CompareTo</c>.</returns>
        /// <seeaalso cref="Coordinate.CompareTo"/>
      	public static Coordinate MinCoordinate(Coordinate[] coordinates)
        {
            Coordinate minCoord = null;
            for (int i = 0; i < coordinates.Length; i++)
            	if (minCoord == null || minCoord.CompareTo(coordinates[i]) > 0)
                    minCoord = coordinates[i];
	        return minCoord;
        }

        /// <summary>
        /// Shifts the positions of the coordinates until <c>firstCoordinate</c> is first.
        /// </summary>
        /// <param name="coordinates">Array to rearrange.</param>
        /// <param name="firstCoordinate">Coordinate to make first.</param>
      	public static void Scroll(Coordinate[] coordinates, Coordinate firstCoordinate)
        {
            int i = IndexOf(firstCoordinate, coordinates);
            if (i < 0) 
                return;
            Coordinate[] newCoordinates = new Coordinate[coordinates.Length];
            Array.Copy(coordinates, i, newCoordinates, 0, coordinates.Length - i);
            Array.Copy(coordinates, 0, newCoordinates, coordinates.Length - i, i);
            Array.Copy(newCoordinates, 0, coordinates, 0, coordinates.Length);
        }

        /// <summary>
        /// Returns the index of <c>coordinate</c> in <c>coordinates</c>.
        /// The first position is 0; the second, 1; etc.
        /// </summary>
        /// <param name="coordinate"><c>Coordinate</c> to search for.</param>
        /// <param name="coordinates">Array to search.</param>
        /// <returns>Position of <c>coordinate</c>, or -1 if it is not found.</returns>
      	public static int IndexOf(Coordinate coordinate, Coordinate[] coordinates)
        {
            for (int i = 0; i < coordinates.Length; i++)
        	    if (coordinate.Equals(coordinates[i]))
                    return i;
            return -1;
      	}
    } 
} 
